内容聚合器应用框架
Django带来了一个高级的聚合生成框架,它使得创建RSS和Atom feeds变得非常容易。
什么是RSS? 什么是Atom?
RSS和Atom都是基于XML的格式,你可以用它来提供有关你站点内容的自动更新的feed。了解更多关于RSS的可以访问 http://www.whatisrss.com/, 更多Atom的信息可以访问 http://www.atomenabled.org/.
想创建一个联合供稿的源(syndication feed),所需要做的只是写一个简短的python类。你可以创建任意多的源(feed)。
高级feed生成框架是一个默认绑定到/feeds/
的视图,Django使用URL的其它部分(在/feeds/
之后的任何东西)来决定输出哪个。
要创建一个 feed
,你只需要写一个 feed
类然后配置你的URLconf指向它。
初始化
为了在您的Django站点中激活syndication feeds, 添加如下的 URLconf:
(r'^feeds/(?P<url>.*)/$', 'django.contrib.syndication.views.feed',
{'feed_dict': feeds}
),
这一行告诉Django使用RSS框架处理所有的以 "feeds/"
开头的URL. ( 你可以修改 "feeds/"
前缀以满足您自己的要求. )
URLConf里有一行参数: {'feed_dict': feeds}
,这个参数可以把对应URL需要发布的feed内容传递给 syndication framework。
特别的,feed_dict
应该是一个映射feed的slug(简短URL标签)到它的Feed
类的字典 你可以在URL配置本身里定义feed_dict
,这里是一个完整的例子:
from django.conf.urls.defaults import *
from mysite.feeds import LatestEntries, LatestEntriesByCategory
feeds = {
'latest': LatestEntries,
'categories': LatestEntriesByCategory,
}
urlpatterns = patterns('',
# ...
(r'^feeds/(?P<url>.*)/$', 'django.contrib.syndication.views.feed',
{'feed_dict': feeds}),
# ...
)
前面的例子注册了两个feeds
:
LatestEntries
表示的内容将对应到feeds/latest/
。LatestEntriesByCategory
的内容将对应到feeds/categories/
。
以上的设定完成之后,接下来需要自己定义 Feed 类
一个 Feed
类是一个简单的python类,用来表示一个syndication feed。一个feed
可能是简单的(例如一个站点新闻feed,或者最基本的,显示一个blog的最新条目),也可能更加复杂(例如一个显示blog某一类别下所有条目的feed
。 这里类别 category 是个变量).
Feed
类必须继承django.contrib.syndication.feeds.Feed
,它们可以在你的代码树的任何位置。
一个简单的Feed
This simple example describes a feed of the latest five blog entries for a given blog:
from django.contrib.syndication.feeds import Feed
from mysite.blog.models import Entry
class LatestEntries(Feed):
title = "My Blog"
link = "/archive/"
description = "The latest news about stuff."
def items(self):
return Entry.objects.order_by('-pub_date')[:5]
要注意的重要的事情如下所示:
- 子类
django.contrib.syndication.feeds.Feed
。 title
,link
, 和description
对应一个标准 RSS 里的<title>
,<link>
, 和<description>
标签。items()
是一个方法,返回一个用以包含在包含在feed的<item>
元素里的list
虽然例子里用Djangos database API返回的 NewsItem 对象,items()
不一定必须返回 model的实例。
还有一个步骤,在一个RSS feed里,每个<item>
有一个<title>
, <link>
,和<description>
,我们需要告诉框架把数据放到这些元素中。
如果要指定
<title>
和<description>
,可以建立一个Django模板,名字叫feeds/latest_title.html
和feeds/latest_description.html
,后者是URLConf里为对应feed指定的slug
。注意.html
后缀是必须的。RSS系统模板渲染每一个条目,需要给传递2个参数给模板上下文变量:
obj
: 当前对象 ( 返回到items()
任意对象之一 )。site
: 一个表示当前站点的django.models.core.sites.Site
对象。这对于{{ site.domain }}
或者{{ site.name }}
很有用。
如果你在创建模板的时候,没有指明标题或者描述信息,框架会默认使用 "{{ obj }}"
,对象的字符串表示。(对于model对象的__unicode__()
方法)
你也可以通过修改 Feed
类中的两个属性 title_template
和 description_template
来改变这两个模板的名字。
你有两种方法来指定
<link>
的内容。 Django 首先执行items()
中每一项的get_absolute_url()
方法。如果该方法不存在,就会尝试执行 Feed 类中的item_link()
方法,并将自身作为item
参数传递进去。get_absolute_url()
和item_link()
都应该以Python字符串形式返回URL。对于前面提到的
LatestEntries
例子,我们可以实现一个简单的feed模板。latest_title.html
包括:{{ obj.title }}
并且 latest_description.html
包含:
{{ obj.description }}
这真是太简单了!
一个更复杂的Feed
框架通过参数支持更加复杂的feeds。
例如:你的博客针对每一个不同的标签提供不同的RSS feed,通常你会将他们分类设置访问入口。如果为每一个单独的区域建立一个 Feed 类就显得很不明智。
取而代之的方法是,使用聚合框架来产生一个通用的源,使其可以根据feeds URL返回相应的信息。
你的具体标签feeds可以像如下这样使用URLs:
- http://example.com/feeds/tags/python/ : 返回"python"标签的feeds
- http://example.com/feeds/tags/cats/ : 返回"cats"标签的feeds
这里slug是"tags".syndication框架会检查slug字段后面的URL位置,也就是'python'和'cats',并且给你一个hook让你告诉它这些URL字段是什么含义以及它们是如何影响返回的feed的。
举个例子会澄清一切。 下面是每个地区特定的feeds:
from django.core.exceptions import ObjectDoesNotExist
from mysite.blog.models import Entry, Tag
class TagFeed(Feed):
def get_object(self, bits):
# In case of "/feeds/tags/cats/dogs/mice/", or other such
# clutter, check that bits has only one member.
if len(bits) != 1:
raise ObjectDoesNotExist
return Tag.objects.get(tag=bits[0])
def title(self, obj):
return "My Blog: Entries tagged with %s" % obj.tag
def link(self, obj):
return obj.get_absolute_url()
def description(self, obj):
return "Entries tagged with %s" % obj.tag
def items(self, obj):
entries = Entry.objects.filter(tags__id__exact=obj.id)
return entries.order_by('-pub_date')[:30]
以下是RSS框架的基本算法,我们假设通过URL /feeds/tags/python/
来访问这个类:
这个框架收到访问
/feeds/tags/python/
的请求并发现在slug字段(tags)后面还有额外的URL字段"/python/"
。它会用"/"
作分割符将slug后面的"python/"分离出来,然后调用Feed类的get_object()
方法,并将"python"当作参数传递进去。这个例子中,bits是
['python']
。如果请求的URL是/feeds/tags/python/django/
,则bits会是['python','django']
。get_object()
方法会根据传递进来的参数(bits)来查询到正确的标签。 这种情况下,通过调用Django database API来查询标签。注意,当参数不争取的时候,get_object()
方法有可能产生django.core.exceptions.ObjectDoesNotExist
异常。在Tag.objects.get()
方法里并没有try/except
,因为这不是必须地。这个方法产生Tag.DoesNotExist
异常,这个异常是ObjectDoesNotExist
的子类。在get_object()
方法中产生一个异常会让Django产生一个404错误。要生成feed的
<title>
,<link>
, 和<description>
,Django使用title()
,link()
, 和description()
方法。在之前的例子中,它们是简单字符串类的属性,但是在这个例子中,证明它既可以是字符串还可以是方法。针对每一个title
,link
, 和description
,Django遵循如下算法:- 它尝试调用一个方法,传递一个obj参数进去,这个obj参数是一个从
get_object()
方法返回的对象。 - 如果尝试失败,Django将不带任何参数再次调用一个方法。
- 如果依然失败,Django会使用类属性。
- 它尝试调用一个方法,传递一个obj参数进去,这个obj参数是一个从
最后,注意在本例中的
items()
方法也带有一个obj参数,针对items的算法和上面描述的一样。首先,会这么调用items(obj)
,然后这么调用items()
,最后使用items
类的属性(是一个list)
Feed
类的所有方法和属性的文档永远可以从Django官方文档找到(http://docs.djangoproject.com/en/dev/ref/contrib/syndication/)
指定Feed的类型
默认情况下, 聚合框架生成RSS 2.0. 要改变这样的情况, 在 Feed 类中添加一个 feed_type 属性:
from django.utils.feedgenerator import Atom1Feed
class MyFeed(Feed):
feed_type = Atom1Feed
注意你把 feed_type
赋值成一个类对象,而不是类实例。 目前合法的Feed类型如表11-1所示。
表 11-1. Feed 类型
Feed 类 | 类型 |
---|---|
django.utils.feedgenerator.Rss201rev2Feed | RSS 2.01 (default) |
django.utils.feedgenerator.RssUserland091Feed | RSS 0.91 |
django.utils.feedgenerator.Atom1Feed | Atom 1.0 |
闭包
为了指定闭包(例如,与feed项比方说MP3 feeds相关联的媒体资源信息),使用 item_enclosure_url
, item_enclosure_length
,以及 item_enclosure_mime_type
,比如:
from myproject.models import Song
class MyFeedWithEnclosures(Feed):
title = "Example feed with enclosures"
link = "/feeds/example-with-enclosures/"
def items(self):
return Song.objects.all()[:30]
def item_enclosure_url(self, item):
return item.song_url
def item_enclosure_length(self, item):
return item.song_length
item_enclosure_mime_type = "audio/mpeg"
当然,你首先要创建一个包含有 song_url
和 song_length
(比如按照字节计算的长度)域的 Song
对象。
语言
聚合框架自动创建的Feed包含适当的 <language>
标签(RSS 2.0) 或 xml:lang
属性(Atom). 他直接来自于您的 LANGUAGE_CODE
设置。
URLs
link
方法/属性可以以绝对URL的形式(例如, "/blog/"
)或者指定协议和域名的URL的形式返回(例如"http://www.example.com/blog/" )。如果 link
没有返回域名,聚合框架会根据 SITE_ID
设置,自动的插入当前站点的域信息。(更多关于SITE_ID和sites框架请查看第16章)
Atom feeds需要 <link rel="self">
指明feeds现在的位置。 syndication 框架将会自动生成。
同时发布Atom and RSS
一些开发人员想同时支持Atom和RSS。这在Django中很容易实现:只需创建一个你的 feed 类的子类,然后修改 feed_type
,并且更新URLconf内容。下面是一个完整的例子:
from django.contrib.syndication.feeds import Feed
from django.utils.feedgenerator import Atom1Feed
from mysite.blog.models import Entry
class RssLatestEntries(Feed):
title = "My Blog"
link = "/archive/"
description = "The latest news about stuff."
def items(self):
return Entry.objects.order_by('-pub_date')[:5]
class AtomLatestEntries(RssLatestEntries):
feed_type = Atom1Feed
这是与之相对应那个的URLconf:
from django.conf.urls.defaults import *
from myproject.feeds import RssLatestEntries, AtomLatestEntries
feeds = {
'rss': RssLatestEntries,
'atom': AtomLatestEntries,
}
urlpatterns = patterns('',
# ...
(r'^feeds/(?P<url>.*)/$', 'django.contrib.syndication.views.feed',
{'feed_dict': feeds}),
# ...
)
{$ activeFileHint $}